package com.qingcloud.fexecutor.service;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.qingcloud.base.api.remote.admin.AdminApi;
import com.qingcloud.base.context.UserContextHolder;
import com.qingcloud.fexecutor.base.enums.ApiTypeEnum;
import com.qingcloud.base.exception.WamingException;
import com.qingcloud.fexecutor.base.handler.ApiHandler;
import com.qingcloud.fexecutor.base.handler.ApiParamDTO;
import com.qingcloud.fexecutor.base.handler.ApiRequestParam;
import com.qingcloud.fexecutor.base.handler.dynamic.DynamicApiHandler;
import com.qingcloud.fexecutor.base.handler.statics.StaticApiHandler;
import com.qingcloud.base.remote.pojo.LoginUserDTO;
import com.qingcloud.fexecutor.model.AppDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.ssssssss.magicapi.core.config.MagicConfiguration;
import org.ssssssss.magicapi.datasource.model.MagicDynamicDataSource;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

import static org.springframework.http.HttpHeaders.*;

/**
 * API执行服务
 */
@Service
@Slf4j
public class ApiInvokeService {

    @Resource
    private AppDataSourceService appDataSourceService;

    @Resource
    private MagicDynamicDataSource dynamicDataSource;

    @Resource
    private MagicConfiguration magicConfiguration;

    @Resource
    private AdminApi adminApi;

    private static final List<String> DEFAULT_ALLOW_READ_RESPONSE_HEADERS = Arrays.asList(
            ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_METHODS,
            CONTENT_TYPE, DATE, SERVER, SET_COOKIE, CONNECTION, CONTENT_LENGTH, CONTENT_ENCODING, TRANSFER_ENCODING, VARY);

    /**
     * 执行API
     */
    public Object invoke(ApiRequestParam param, HttpServletRequest request, HttpServletResponse response) throws Throwable {
        ApiHandler handler;
        if (ApiTypeEnum.STATIC.code.equals(param.getType())) {
            //静态API
            handler = new StaticApiHandler(param.getUrl(), param.getMethod());
        } else {
            //动态API，检查数据源
            validDatasource(param.getDsId().toString());
            handler = new DynamicApiHandler(magicConfiguration, param.getDsId().toString(), param.getScript());
        }
        //不同类型参数
        Map<String, String> headers = convertHeaderParam(param.getHeadParam());
        Map<String, Object> querys = convertParam(param.getQueryParam());
        Map<String, Object> forms = convertParam(param.getFormParam());
        Map<String, Object> paths = convertParam(param.getPathParam());
        Object bodyValue = null;
        if (StrUtil.isNotBlank(param.getApiBodyJson())) {
            if (param.getApiBodyJson().trim().startsWith("[")) {
                JSONArray jsonArray = JSONArray.parseArray(param.getApiBodyJson());
                if (!CollUtil.isEmpty(jsonArray)) {
                    bodyValue = jsonArray;
                }
            }
            if (param.getApiBodyJson().trim().startsWith("{")) {
                JSONObject jsonObject = JSONObject.parseObject(param.getApiBodyJson());
                if (CollUtil.isNotEmpty(jsonObject)) {
                    for (Entry<String, Object> entry : jsonObject.entrySet()) {
                        if (Objects.nonNull(entry.getValue()) && StrUtil.isNotBlank(entry.toString())) {
                            if (String.valueOf(entry.getValue()).startsWith("[")) {
                                jsonObject.put(entry.getKey(), JSONObject.parseArray(String.valueOf(entry.getValue())));
                            }
                            if (String.valueOf(entry.getValue()).startsWith("{")) {
                                jsonObject.put(entry.getKey(), JSONObject.parseObject(String.valueOf(entry.getValue())));
                            }
                        }
                    }
                    bodyValue = jsonObject;
                }
            }
        }
        //用户信息
        LoginUserDTO userInfo = UserContextHolder.getUserInfo();
        if (Objects.nonNull(userInfo)) {
            LoginUserDTO loginUserDTO = adminApi.findUserById(userInfo.getUserId());
            UserContextHolder.setUserInfo(loginUserDTO);
        }
        return handler.invoke(request, response, headers, querys, forms, paths, bodyValue);
    }

    /**
     * 检查数据源
     */
    private void validDatasource(String datasourceKey) {
        if (!dynamicDataSource.datasources().contains(datasourceKey)) {
            AppDataSource database = appDataSourceService.findUseInfo(Integer.parseInt(datasourceKey));
            DruidDataSource datasource = new DruidDataSource();
            datasource.setUrl(database.getJdbcUrl());
            datasource.setUsername(database.getUsername());
            datasource.setPassword(database.getPassword());
            datasource.setInitialSize(1);
            datasource.setMinIdle(1);
            datasource.setMaxActive(3);
            datasource.setMaxWait(60000);
            datasource.setTimeBetweenEvictionRunsMillis(600000);
            datasource.setValidationQuery("SELECT 1");
            datasource.setTestWhileIdle(true);
            datasource.setTestOnBorrow(true);
            datasource.setTestOnReturn(false);
            datasource.setRemoveAbandonedTimeout(3600);
            dynamicDataSource.put(datasourceKey, datasource);
        }
    }

    /**
     * API参数转换
     */
    public Map<String, String> convertHeaderParam(List<ApiParamDTO> dtoList) {
        Map<String, String> map = new HashMap<>(dtoList.size());
        for (ApiParamDTO it : dtoList) {
            if (it.isRequiredFlag() && it.getDefaultValue() == null) {
                throw new WamingException(it.getKey() + "参数必填");
            }
            map.put(it.getKey(), StrUtil.toString(it.getDefaultValue()));
        }
        return map;
    }

    /**
     * API参数转换
     */
    public Map<String, Object> convertParam(List<ApiParamDTO> dtoList) {
        Map<String, Object> map = new HashMap<>(dtoList.size());
        for (ApiParamDTO it : dtoList) {
            if (it.isRequiredFlag() && it.getDefaultValue() == null) {
                throw new WamingException(it.getKey() + "参数必填");
            }
            map.put(it.getKey(), it.getDefaultValue());
        }
        return map;
    }

}
